﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Asn1;

namespace TheClerk.Forms.Templates
{
    class Envelope
    {
        ///<summary>The bundle which encloses the Data</summary>
        ///<remarks>
        /// Has contents.
        /// Has recipient.
        /// 
        /// Overall design: envelope is an X509V3Certificate.
        /// It's packaged with its letter openers.
        /// 1.3.6.1.4.1.22232.7: TheClerk
        /// 1.3.6.1.4.1.22232.7.0: Definitions
        /// 1.3.6.1.4.1.22232.7.0.0: Value-type
        /// 1.3.6.1.4.1.22232.7.0.1: Value
        /// 1.3.6.1.4.1.22232.7.1: Form
        /// 1.3.6.1.4.1.22232.7.1.0: envelope
        /// 1.3.6.1.4.1.22232.7.1.0.0: OpaquePayload
        /// 1.3.6.1.4.1.22232.7.1.0.1: StrongBox
        /// 1.3.6.1.4.1.22232.7.1.0.2: CopyrightLicense
        /// 1.3.6.1.4.1.22232.7.1.0.2.0: human-legible
        /// 1.3.6.1.4.1.22232.7.1.0.2.1: machine-legible
        /// 1.3.6.1.4.1.22232.7.1.0.3: Stamps
        ///</remarks>

        X509V3CertificateGenerator CertificateGenerator =
            new X509V3CertificateGenerator();

        #region Private fields

        /// <summary>
        /// List of sender credentials to use
        /// </summary>
        private System.Collections.Generic.List<TheClerk.Access.Identity> senderID;
        /// <summary>
        /// List of recipients to send to
        /// </summary>
        private System.Collections.Generic.List<TheClerk.Access.Identity> recipientID;

        private System.Collections.Generic.List<System.IO.Stream> Contents;

        /// <summary>
        /// The symmetric key used to encrypt the Payload
        /// </summary>
        private byte[] EvidenceKey = new byte[256/8]; // AES256 divided by 8 bits per byte

        #endregion

        internal Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair EnvelopeKeypair { get; private set; }

        #region Constructor
        /// <summary>
        /// Build an Envelope object
        /// </summary>
        /// <param name="RecipientIdentity">The first identity (public key) this is destined to</param>
        /// <param name="SenderIdentity">The identity (public key) to sign with the private key of</param>
        /// <param name="inputStream">the first content to include in the Envelope</param>
        /// <param name="mimeType">The MIME type of the content shortcut-included (throws if null)</param>
        /// <remarks>
        /// Every envelope has one recipient.  Every envelope also has one sender.  Singletons are bad.
        /// So, the senderID and recipientID are The 'sender' in this case is an asymmetric nonce if it's null.
        /// </remarks>
        Envelope(
            TheClerk.Access.Identity RecipientIdentity=null,
            TheClerk.Access.Identity SenderIdentity=null,
            System.IO.Stream inputStream = null,
            string mimeType = "application/octet-stream")
        {
            // 
            if (mimeType == null)
                throw new ArgumentNullException("mimeType");

            // Set the mandatory fields of the Certificate
            SetupCertificateGenerator();

            // Securely set EvidenceKey with new random material
            (new Org.BouncyCastle.Security.SecureRandom(
                     new Org.BouncyCastle.Crypto.Prng.CryptoApiRandomGenerator())).NextBytes(EvidenceKey);

            if (null != inputStream) {
                AddOpaqueContent(inputStream, mimeType);
            }
        }
        #endregion


        private System.Collections.Generic.List<TheClerk.Forms.Field> OpaqueContent;
        private System.Collections.Generic.List<TheClerk.Forms.Field> TransparentContent;
        private System.Collections.Generic.List<TheClerk.Forms.Field> LegibleCopyrightLicense;
        private System.Collections.Generic.List<TheClerk.Forms.Field> MachineLegibleCopyright;

        public void AddOpaqueContent(System.IO.Stream inputStream, string mimeType = "application/binary")
        {
            OpaqueContent.Add(new ContentField(mimeType,inputStream));
        }

        public void AddTransparentContent(System.IO.Stream inputStream, string mimeType = "text/plain")
        {
            TransparentContent.Add(new ContentField(mimeType,inputStream));
        }

        public void SetLegibleCopyrightLicense(System.IO.Stream inputStream, string mimeType = "text/x-copyright-License")
        {
            TransparentContent.Add(new ContentField(mimeType, inputStream));
        }

        private void SetupCertificateGenerator()
        {
            // Set the generator's static parameters (none of these matter)
            CertificateGenerator.SetSerialNumber(Org.BouncyCastle.Math.BigInteger.One);
            CertificateGenerator.SetIssuerDN(
                new Org.BouncyCastle.Asn1.X509.X509Name("C=XX")
                );
            CertificateGenerator.SetSubjectDN(
                new Org.BouncyCastle.Asn1.X509.X509Name("C=XX")
                );
            CertificateGenerator.SetSignatureAlgorithm("ECDSA");
            CertificateGenerator.SetNotBefore(DateTime.UtcNow);
            // TODO revisit validity time
            CertificateGenerator.SetNotAfter(DateTime.UtcNow.AddDays(30));

            // Generate the new throwaway keypair...
            Org.BouncyCastle.Crypto.IAsymmetricCipherKeyPairGenerator keygen =
                new Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator();
            keygen.Init(
                new Org.BouncyCastle.Crypto.KeyGenerationParameters(
                    new Org.BouncyCastle.Security.SecureRandom(
                        new Org.BouncyCastle.Crypto.Prng.CryptoApiRandomGenerator()
                    ),
                // TODO make this a tuneable parameter
                    384)  // 256 to protect info up to SECRET, 384 to protect TOP SECRET
                          // (source: http://keylength.com/en/6)
                          // TODO find place to set hash length for signature algorithm
                );

            EnvelopeKeypair = keygen.GenerateKeyPair();

            // ...and associate it with the Certificate generator
            CertificateGenerator.SetPublicKey(EnvelopeKeypair.Public);

            // All mandatory fields have now been set, and it's up to the
            // rest of the code to fill in the Extension structure.
        }
    }
}
